
#include<android/log.h>
#include <android/bitmap.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
//#include <thread>     //c11有标准线程
//#include <linux/socket.h> //tcp.h

#include "decoder/LogUtil.h"

#include <cstdio>
#include <cstring>
#include <FFMediaPlayer.h>
#include "decoder/LogUtil.h"
#include "jni.h"

extern "C" {
#include <libavcodec/version.h>
#include <libavcodec/avcodec.h>
#include <libavformat/version.h>
#include <libavutil/version.h>
#include <libavfilter/version.h>
#include <libswresample/version.h>
#include <libswscale/version.h>
}

void ThrowException(JNIEnv *env, const char *exception, const char *message) {
    jclass clazz = env->FindClass(exception);
    if (NULL != clazz) {
        env->ThrowNew(clazz, message);
    }
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_hb_anative_NativeWindowSample_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_drawColor(JNIEnv *env, jobject obj, jobject surface,
                                                 jint colorARGB) {
    //分离ARGB
    int alpha = (colorARGB >> 24) & 0xFF;
    int red = (colorARGB >> 16) & 0xFF;
    int green = (colorARGB >> 8) & 0xFF;
    int blue = colorARGB & 0xFF;

    int colorABGR = (alpha << 24) | (blue << 16) | (green << 8) | red;

    //获取目标surface
    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
    if (NULL == window) {
        ThrowException(env, "java/lang/RuntimeException", "unable to get native window");
        return;
    }
    //默认的是RGB_565
    int32_t result = ANativeWindow_setBuffersGeometry(window, 640, 640, WINDOW_FORMAT_RGBA_8888);
    if (result < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to set buffers geometry");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }
    ANativeWindow_acquire(window);

    ANativeWindow_Buffer buffer;
    if (ANativeWindow_lock(window, &buffer, NULL) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to lock native window");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }

    uint32_t *line = (uint32_t *) buffer.bits;
    LOGCATE("width:%d height:%d stride:%d", buffer.width, buffer.height, buffer.stride);

/*    for (int y = 0; y < buffer.height; y++) {
        for (int x = 0; x < buffer.width; x++) {
            line[x] = colorABGR;
        }
        line = line + buffer.stride;
    }

    memset(line, colorABGR, buffer.height * buffer.width);*/

    for (int i = 0; i < buffer.height * buffer.width; ++i) {
        line[i] = colorABGR;
    }

    if (ANativeWindow_unlockAndPost(window) < 0) {
        ThrowException(env, "java/lang/RuntimeException",
                       "unable to unlock and post to native window");
    }
    //释放窗口
    ANativeWindow_release(window);

}
extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_drawBitmap(JNIEnv *env, jobject thiz, jobject surface,
                                                  jobject bitmap) {

    LOGCATE("avcondec version:%s", AV_STRINGIFY(LIBAVCODEC_VERSION));
    //获取bitmap的信息,比如宽和高
    AndroidBitmapInfo info;
    if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to get bitmap info");
        return;
    }

    char *data = NULL;
    //获取bitmap对应的native指针
    if (AndroidBitmap_lockPixels(env, bitmap, (void **) &data) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to lock pixels");
        return;
    }
    if (AndroidBitmap_unlockPixels(env, bitmap) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to unlock pixels");
        return;
    }

    //获取目标surface
    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
    if (NULL == window) {
        ThrowException(env, "java/lang/RuntimeException", "unable to get native window");
        return;
    }
    int32_t width = ANativeWindow_getWidth(window);
    int32_t height = ANativeWindow_getHeight(window);

    LOGCATE("bitmap : width:%d height:%d stride:%d", info.width, info.height, info.stride);
    LOGCATE("Windows : width:%d height:%d ", width, height);
    //这里设置为RGBA的方式,总共是4字节32位
    int32_t result = ANativeWindow_setBuffersGeometry(window, 0, 0,
                                                      WINDOW_FORMAT_RGBA_8888);
    LOGCATE("after bitmap : width:%d height:%d stride:%d", info.width, info.height, info.stride);
    LOGCATE("after Windows : width:%d height:%d ", width, height);
    if (result < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to set buffers geometry");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }
    ANativeWindow_acquire(window);

    ANativeWindow_Buffer buffer;
    //锁定窗口的绘图表面
    if (ANativeWindow_lock(window, &buffer, NULL) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to lock native window");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }

    //转换为像素点来处理
//    int32_t *bitmapPixes = (int32_t *) data;
    int32_t *line = (int32_t *) buffer.bits;
/*    for (int y = 0; y < buffer.height; y++) {
        for (int x = 0; x < buffer.width; x++) {
            line[x] = bitmapPixes[buffer.height * y + x];
        }
        line = line + buffer.stride;
    }*/

    for (int i = 0; i < info.height; ++i) {
//        memcpy(line[i*buffer.width], bitmapPixes[i * buffer.width], buffer.width);
        memcpy(line + i * buffer.stride, data + i * info.width * 4, info.width * 4);
        if (i < info.height) {
            memcpy(line + i * buffer.stride + width - info.width, data + i * info.width * 4,
                   info.width * 4);
        }
    }

    //解锁窗口的绘图表面
    if (ANativeWindow_unlockAndPost(window) < 0) {
        ThrowException(env, "java/lang/RuntimeException",
                       "unable to unlock and post to native window");
    }
    //释放
    ANativeWindow_release(window);
}

char *ConvertJByteaArrayToChars(JNIEnv *env, jbyteArray bytearray) {

    char *chars = NULL;
    jbyte *bytes;
    bytes = env->GetByteArrayElements(bytearray, 0);
    int chars_len = env->GetArrayLength(bytearray);
    chars = new char[chars_len + 1];
    memset(chars, 0, chars_len + 1);
    memcpy(chars, bytes, chars_len);
    chars[chars_len] = 0;

    env->ReleaseByteArrayElements(bytearray, bytes, 0);

    return chars;
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_hb_anative_NativeWindowSample_nativeBitmap(JNIEnv *env, jobject thiz, jobject bitmap) {
    //获取bitmap的信息,比如宽和高
    AndroidBitmapInfo info;
    if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to get bitmap info");
        return nullptr;
    }

    char *data = NULL;
    //获取bitmap对应的native指针
    if (AndroidBitmap_lockPixels(env, bitmap, (void **) &data) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to lock pixels");
        return nullptr;
    }
    if (AndroidBitmap_unlockPixels(env, bitmap) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to unlock pixels");
        return nullptr;
    }

/*    uint32_t length = info.width * info.height * 4;
    jbyteArray pArray = env->NewByteArray(length);

    env->SetByteArrayRegion(pArray, 0, length, (jbyte *) data);*/

    int len = info.width * info.height;
    jintArray intArray = env->NewIntArray(len);
    jboolean isCopy = 0;
    jint *pInt = env->GetIntArrayElements(intArray, &isCopy);

/*
    //转换为像素点来处理
    int32_t *bitmapPixes = (int32_t *) data;
    for (int i = 0; i < len; i++) {
        pInt[i] = bitmapPixes[i];
    }
*/

    LOGCATE("width:%d height:%d stride:%d", info.width, info.height, info.stride);
    for (int i = 0; i < info.height; ++i) {
//        memcpy(line[i*buffer.width], bitmapPixes[i * buffer.width], buffer.width);
        memcpy(pInt + i * info.width, data + i * info.width * 4, info.width * 4);
    }

    env->SetIntArrayRegion(intArray, 0, len, pInt);

//    取值 零(0) 时，更新数组并释放所有元素;
//    取值 JNI_COMMIT 时，更新但不释放所有元素;
//    取值 JNI_ABORT 时，不作更新但释放所有元素；
    env->ReleaseIntArrayElements(intArray, pInt, 0);
    return intArray;
}

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_hb_anative_NativeWindowSample_nativeBitmapArray(JNIEnv *env, jobject thiz, jintArray arr) {
    jboolean jbIsCopy = JNI_FALSE;
    jint *elements = env->GetIntArrayElements(arr, &jbIsCopy);
    int len = env->GetArrayLength(arr);
    jbyte *data = new jbyte[len * 4 + 1];
    memset(data, 0, len * 4 + 1);
    memcpy(data, (jbyte *) elements, len * 4);

    env->ReleaseIntArrayElements(arr, elements, 0);

    jbyteArray pArray = env->NewByteArray(len * 4);
    env->SetByteArrayRegion(pArray, 0, len * 4, data);
    delete[] data;
    return pArray;
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_hb_anative_NativeWindowSample_nativeBitmapIntArray(JNIEnv *env, jobject thiz,
                                                            jintArray arr) {

    jsize length = env->GetArrayLength(arr);
    jintArray intArray = env->NewIntArray(length);
    jint *pInt = env->GetIntArrayElements(arr, 0);
    env->SetIntArrayRegion(intArray, 0, length, pInt);

    env->ReleaseIntArrayElements(arr, pInt, 0);
    return intArray;
}


extern "C"
JNIEXPORT jstring JNICALL
Java_com_hb_anative_NativeWindowSample_native_1GetFFmpegVersion(JNIEnv *env, jobject thiz) {
    char strBuffer[1024 * 4] = {0};
    strcat(strBuffer, "libavcodec : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVCODEC_VERSION));
    strcat(strBuffer, "\nlibavformat : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVFORMAT_VERSION));
    strcat(strBuffer, "\nlibavutil : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVUTIL_VERSION));
    strcat(strBuffer, "\nlibavfilter : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVFILTER_VERSION));
    strcat(strBuffer, "\nlibswresample : ");
    strcat(strBuffer, AV_STRINGIFY(LIBSWRESAMPLE_VERSION));
    strcat(strBuffer, "\nlibswscale : ");
    strcat(strBuffer, AV_STRINGIFY(LIBSWSCALE_VERSION));
    strcat(strBuffer, "\navcodec_configure : \n");
    strcat(strBuffer, avcodec_configuration());
    strcat(strBuffer, "\navcodec_license : ");
    strcat(strBuffer, avcodec_license());
    LOGCATE("GetFFmpegVersion\n%s", strBuffer);

    return env->NewStringUTF(strBuffer);
}


extern "C"
JNIEXPORT jlong JNICALL
Java_com_hb_anative_NativeWindowSample_native_1Init(JNIEnv *env, jobject obj, jstring jurl,
                                                    jint renderType, jobject surface) {
    const char *url = env->GetStringUTFChars(jurl, nullptr);
    FFMediaPlayer *player = new FFMediaPlayer();
    player->Init(env, obj, const_cast<char *>(url), renderType, surface);
    env->ReleaseStringUTFChars(jurl, url);
    return reinterpret_cast<jlong>(player);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_native_1Play(JNIEnv *env, jobject thiz,
                                                    jlong player_handle) {
    if (player_handle != 0) {
        FFMediaPlayer *ffMediaPlayer = reinterpret_cast<FFMediaPlayer *>(player_handle);
        ffMediaPlayer->Play();
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_native_1SeekToPosition(JNIEnv *env, jobject thiz,
                                                              jlong player_handle,
                                                              jfloat position) {
    if (player_handle != 0) {
        FFMediaPlayer *ffMediaPlayer = reinterpret_cast<FFMediaPlayer *>(player_handle);
        ffMediaPlayer->SeekToPosition(position);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_native_1Pause(JNIEnv *env, jobject thiz,
                                                     jlong player_handle) {
    if (player_handle != 0) {
        FFMediaPlayer *ffMediaPlayer = reinterpret_cast<FFMediaPlayer *>(player_handle);
        ffMediaPlayer->Pause();
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_native_1Stop(JNIEnv *env, jobject thiz,
                                                    jlong player_handle) {
    if (player_handle != 0) {
        FFMediaPlayer *ffMediaPlayer = reinterpret_cast<FFMediaPlayer *>(player_handle);
        ffMediaPlayer->Stop();
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_native_1UnInit(JNIEnv *env, jobject thiz,
                                                      jlong player_handle) {
    if (player_handle != 0) {
        FFMediaPlayer *ffMediaPlayer = reinterpret_cast<FFMediaPlayer *>(player_handle);
        ffMediaPlayer->UnInit();
    }
}

extern "C"
JNIEXPORT jlong JNICALL
Java_com_hb_anative_NativeWindowSample_native_1GetMediaParams(JNIEnv *env, jobject thiz,
                                                              jlong player_handle,
                                                              jint param_type) {
    long value = 0;
    if (player_handle != 0) {
        FFMediaPlayer *ffMediaPlayer = reinterpret_cast<FFMediaPlayer *>(player_handle);
        value = ffMediaPlayer->GetMediaParams(param_type);
    }
    return value;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_hb_anative_NativeWindowSample_native_1Bitmap(JNIEnv *env, jobject thiz,
                                                      jlong player_handle, jobject bitmap) {
    if (player_handle != 0) {
        FFMediaPlayer *ffMediaPlayer = reinterpret_cast<FFMediaPlayer *>(player_handle);

        //获取bitmap的信息,比如宽和高
        AndroidBitmapInfo info;
        if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
            ThrowException(env, "java/lang/RuntimeException", "unable to get bitmap info");
            return;
        }

        char *data = NULL;
        //获取bitmap对应的native指针
        if (AndroidBitmap_lockPixels(env, bitmap, (void **) &data) < 0) {
            ThrowException(env, "java/lang/RuntimeException", "unable to lock pixels");
            return;
        }
        if (AndroidBitmap_unlockPixels(env, bitmap) < 0) {
            ThrowException(env, "java/lang/RuntimeException", "unable to unlock pixels");
            return;
        }

        uint32_t len = info.width * info.height * 4;
        char *bm = new char[len];
        memcpy(bm, data, len);
        ffMediaPlayer->setBitmap(data, info.width, info.height);
    }
}